package top.atcra.rpc.protection.circuitBreaker.impl;

import lombok.extern.slf4j.Slf4j;
import top.atcra.rpc.protection.circuitBreaker.CircuitBreaker;
import top.atcra.rpc.protection.circuitBreaker.counter.ErrorRateCounter;
import top.atcra.rpc.protection.circuitBreaker.counter.FixWindowErrorRateCounter;

import java.util.TimerTask;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

@Slf4j
public class ProportionCircuitBreaker extends CircuitBreaker {
    // 滑动窗口错误计数器
    private final ErrorRateCounter errorRateCounter;

    // 熔断阈值，当错误率超过该阈值时，触发熔断
    private double error_rate_threshold = 0.5;
    // 统计周期，单位为毫秒
    private int period_ms = 3000;

    // 上次熔断重置时间
    private long lastResetTime;
    // 重置任务线程池
    private final ScheduledExecutorService executor = Executors.newScheduledThreadPool(2,
            runnable -> {
                Thread thread = new Thread(runnable);
                thread.setDaemon(true);
                return thread;
            }
    );

    public ProportionCircuitBreaker(double error_rate_threshold, int period_ms) {
        this.error_rate_threshold = error_rate_threshold;
        this.period_ms = period_ms;
        // 将一个周期分成10个时间片，统计整个窗口的错误率
        errorRateCounter = new FixWindowErrorRateCounter(period_ms);
    }

    @Override
    public State getState() {
        // 计算错误率
        double er = errorRateCounter.getErrorRate();
        if ( er> error_rate_threshold) {
            log.warn("{}->OPEN: Because of error rate ({}) is too high.",state,er);
            this.state = State.OPEN;
            delayReset(System.currentTimeMillis(), 3000);
        }

        return state;
    }

    @Override
    public void delayReset(long currentTime, int delay_ms) {
        if (currentTime - lastResetTime < delay_ms) {
            return;
        }
        lastResetTime = currentTime;
        var task = new TimerTask() {
            @Override
            public void run() {
                state = State.HALF_OPEN;
            }
        };
        executor.schedule(task, delay_ms, TimeUnit.MILLISECONDS);
    }

    @Override
    public void logCallResult(CompletableFuture<?> callFuture) {
        callFuture.exceptionally(throwable -> {
            errorRateCounter.recordRequest(false);
            if (this.state == State.HALF_OPEN){
                this.state = State.OPEN;
                log.warn("HALF->OPEN: Because of call failure.");
            }
            return null;
        }).thenAccept(result -> {
            errorRateCounter.recordRequest(true);
            if (this.state == State.HALF_OPEN) {
                this.state = State.CLOSED;
                log.debug("HALF->CLOSED: Because of call success.");
            }
        });

    }

}
